#pragma once

#include "GlobalShader.h"

namespace RenderSystem
{
	class Material;
	class MeshMaterialShaderMap;
	class VertexFactory;

	/**
	* A shader meta type for material-linked shaders.
	*/
	class MaterialShaderType : public ShaderType
	{
	public:

		/**
		* Finds a MaterialShaderType by name.
		*/
		static MaterialShaderType* GetTypeByName(const String& TypeName);

		struct CompiledShaderInitializerType 
			: GlobalShaderType::CompiledShaderInitializerType
		{
			const Material* m_pMaterial;

			CompiledShaderInitializerType(
				ShaderType* InType,
				const ShaderCompilerOutput& CompilerOutput,
				const Material* InMaterial
				):
			GlobalShaderType::CompiledShaderInitializerType(InType,CompilerOutput),
				m_pMaterial(InMaterial)
			{}
		};
		typedef Shader* (*ConstructCompiledType)(const CompiledShaderInitializerType&);


		MaterialShaderType(
			const TCHAR* InName,
			const TCHAR* InSourceFilename,
			const TCHAR* InFunctionName,
			DWORD InFrequency,
			ConstructSerializedType InConstructSerializedRef,
			ConstructCompiledType InConstructCompiledRef,
			ModifyCompilationEnvironmentType InModifyCompilationEnvironmentRef
			):
		ShaderType(InName,InSourceFilename,InFunctionName,InConstructSerializedRef,InModifyCompilationEnvironmentRef),
			m_ConstructCompiledType(InConstructCompiledRef)
		{}

		/**
		* Enqueues a compilation for a new shader of this type.
		* @param Material - The material to link the shader with.
		* @param MaterialShaderCode - The shader code for the material.
		*/
		void MaterialShaderType::BeginCompileShader(
			const Material* pMaterial,
			const TCHAR* pszMaterialShaderCode,
			const ShaderProfile& rShaderProfile
			);

		Shader* FinishCompileShader( const Material* pMaterial, const ShaderCompilerOutput& rOutput  );

		// Dynamic casting.
		virtual MaterialShaderType* GetMaterialShaderType() { return this; }

	private:

		ConstructCompiledType	m_ConstructCompiledType;
	};

	/**
	* The set of material shaders for a single material.
	*/
	class MaterialShaderMap : public TShaderMap<MaterialShaderType>
	{
	public:

		typedef std::vector< MaterialShaderMap* >						MaterialShaderMaps;	
		typedef MaterialShaderMaps::iterator							MaterialShaderMapsIterator;	
		typedef MaterialShaderMaps::const_iterator						MaterialShaderMapsConstIterator;

		typedef std::vector< MeshMaterialShaderMap* >					MeshMaterialShaderMaps;
		typedef MeshMaterialShaderMaps::iterator						MeshMaterialShaderMapsIterator;
		typedef MeshMaterialShaderMaps::const_iterator					MeshMaterialShaderMapsConstIterator;

		MaterialShaderMap();
		// Destructor.
		virtual ~MaterialShaderMap();

		/**
		* Compiles the shaders for a material and caches them in this shader map.
		*/
		bool Compile(const Material* pMaterial, const TCHAR* pszMaterialShaderCode);

		/**
		* Begins initializing the shaders used by the material shader map.
		*/
		void BeginInit();

		/**
		* Begins releasing the shaders used by the material shader map.
		*/
		void BeginRelease();

		/**
		* Merges in OtherMaterialShaderMap's shaders and FMeshMaterialShaderMaps
		*/
		void Merge(const MaterialShaderMap* pOtherMaterialShaderMap);

		//accessor
		const MeshMaterialShaderMap* GetMeshShaderMap(VertexFactoryType* VertexFactoryType) const;

	private:

		/** A global map from a material's static parameter set to any shader map cached for that material. */
		static MaterialShaderMaps g_sMaterialShaderMap;

		/** The material's cached shaders for vertex factory type dependent shaders. */
		MeshMaterialShaderMaps m_MeshShaderMaps;

		/** The platform this shader map was compiled with */
		ShaderProfile m_objProfile;

		/** A map from vertex factory type to the material's cached shaders for that vertex factory type. */
		std::map<VertexFactoryType*, MeshMaterialShaderMap*> m_VertexFactoryMap;

		/**
		* Initializes VertexFactoryMap from the contents of MeshShaderMaps.
		*/
		void _InitVertexFactoryMap();
	};

}